﻿using System;
using System.Net.Sockets;
using System.Threading;
using Net.Event;
using Net.System;

namespace Net.Server
{
    class UserToken<Player> where Player : ClientPeerBase
    {
        internal Player client;
        internal ISegment segment;
    }

    /// <summary>
    /// tcp 输入输出完成端口服务器
    /// <para>Player:当有客户端连接服务器就会创建一个Player对象出来, Player对象和XXXClient是对等端, 每当有数据处理都会通知Player对象. </para>
    /// <para>Scene:你可以定义自己的场景类型, 比如帧同步场景处理, mmorpg场景什么处理, 可以重写Scene的Update等等方法实现每个场景的更新和处理. </para>
    /// </summary>
    public class TcpServerIocp<Player, Scene> : TcpServer<Player, Scene> where Player : ClientPeerBase, new() where Scene : SceneBase<Player>, new()
    {
        protected override void StartProcessAccept()
        {
            base.StartProcessAccept();
            InitAsyncEventArgs();
        }

        protected override void ProcessAccept()
        {
            var acceptList = new FastList<Socket>();
            while (IsRunServer)
            {
                try
                {
                    if (Clients.Count < Config.OnlineLimit + Config.LineUp)
                    {
                        Thread.Sleep(1);
                        continue;
                    }
                    if (Server.Poll(performance, SelectMode.SelectRead))
                    {
                        var socket = Server.Accept();
                        socket.ReceiveTimeout = (int)Config.ReconnectionTimeout;
                        acceptList.Add(socket);
                    }
                    else Thread.Sleep(1);
                    CheckAcceptList(acceptList);
                }
                catch (Exception ex)
                {
                    NDebug.LogError($"接受异常:{ex}");
                }
            }
        }

        private void InitAsyncEventArgs()
        {
            for (int i = 0; i < Config.OnlineLimit + Config.LineUp; i++)
            {
                var socketAsyncEventArgs = new SocketAsyncEventArgs();
                var buffer = new byte[Config.ReceiveBufferSize];
                var userToken = new UserToken<Player>()
                {
                    segment = BufferPool.NewSegment(buffer, 0, buffer.Length)
                };
                socketAsyncEventArgs.UserToken = userToken;
                socketAsyncEventArgs.RemoteEndPoint = null;
                socketAsyncEventArgs.SetBuffer(buffer, 0, buffer.Length);
                socketAsyncEventArgs.Completed += OnNetworkCompleted;
                StartAccept(socketAsyncEventArgs);
            }
        }

        private void StartAccept(SocketAsyncEventArgs socketAsyncEventArgs)
        {
            if (!IsRunServer)
                return;
            socketAsyncEventArgs.AcceptSocket = null;
            if (!Server.AcceptAsync(socketAsyncEventArgs))
                OnNetworkCompleted(null, socketAsyncEventArgs);
        }

        protected override void AcceptHander(Player client, params object[] args)
        {
            if (args == null || args.Length == 0)
                return;
            var socketAsyncEventArgs = args[0] as SocketAsyncEventArgs;
            var userToken = socketAsyncEventArgs.UserToken as UserToken<Player>;
            userToken.client = client;
            client.AsyncEventArgs = socketAsyncEventArgs;
            client.AsyncEventArgs.RemoteEndPoint = client.Client.RemoteEndPoint;
        }

        protected void OnNetworkCompleted(object sender, SocketAsyncEventArgs e)
        {
            try
            {
                switch (e.LastOperation)
                {
                    case SocketAsyncOperation.Accept:
                        ProcessAccept(e);
                        break;
                    case SocketAsyncOperation.Receive:
                        ProcessReceive(e);
                        break;
                    case SocketAsyncOperation.Disconnect:
                        CloseClientSocket(e);
                        break;
                }
            }
            catch (Exception ex)
            {
                NDebug.LogError($"IOCP处理异常:{ex}");
            }
        }

        private void ProcessAccept(SocketAsyncEventArgs e)
        {
            var userToken = e.UserToken as UserToken<Player>;
            int count = e.BytesTransferred;
            var segment = userToken.segment;
            segment.Position = e.Offset;
            segment.Count = count;
            if (count > 0 && e.SocketError == SocketError.Success)
            {
                trafficStatistics.ReceivedPacketsPerSecond++;
                trafficStatistics.ReceivedBytesPerSecond += count;
                CheckReconnect(e.AcceptSocket, segment, e);
            }
            if (!e.AcceptSocket.ReceiveAsync(e))
                OnNetworkCompleted(null, e);
        }

        private void ProcessReceive(SocketAsyncEventArgs e)
        {
            var userToken = e.UserToken as UserToken<Player>;
            var client = userToken.client;
            int count = e.BytesTransferred;
            var segment = userToken.segment;
            segment.Position = e.Offset;
            segment.Count = count;
            if (count == 0 || e.SocketError != SocketError.Success)
            {
                CloseClientSocket(e);
                return;
            }
            count = segment.Length;
            Socket clientSocket;
            if (client != null)
            {
                client.ReceiveHandler(ref segment);
                if (count != segment.Length)
                {
                    userToken.segment = segment;
                    e.SetBuffer(segment.Buffer, 0, segment.Length);
                }
                clientSocket = client.Client;
                if (!clientSocket.Connected)
                {
                    CloseClientSocket(e);
                    return;
                }
            }
            else
            {
                clientSocket = e.AcceptSocket;
                CheckReconnect(clientSocket, segment, e);
            }
            if (!clientSocket.ReceiveAsync(e))
                OnNetworkCompleted(null, e);
        }

        private static void CloseClientSocket(SocketAsyncEventArgs e)
        {
            var userToken = e.UserToken as UserToken<Player>;
            if (userToken.client != null && !userToken.client.IsDispose) //客户端主动关闭不需要触发连接中断了
                userToken.client.ConnectLost(SocketError.Disconnecting);
            userToken.client = null;
        }

        protected override void OnClientDispose(Player client)
        {
            if (client.AsyncEventArgs == null)
                return;
            StartAccept(client.AsyncEventArgs);
            client.AsyncEventArgs = null;
        }
    }

    /// <summary>
    /// 默认tcpiocp服务器，当不需要处理Player对象和Scene对象时可使用
    /// </summary>
    public class TcpServerIocp : TcpServerIocp<ClientPeerBase, SceneBase<ClientPeerBase>> { }
}